package com.liziguo.lzgfp.server.netty;

import com.alibaba.fastjson.JSONObject;
import com.liziguo.lzgfp.server.record.ForwardRecord;
import com.liziguo.lzgfp.server.util.GlobalUtil;
import com.liziguo.lzgfp.system.handler.ForWardHandler;
import com.liziguo.lzgfp.system.util.RanStrUtil;
import com.liziguo.lzgfp.system.handler.TemporaryStorageHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.TimeUnit;

/**
 * @Author: Liziguo
 * @Date: 2021/12/24 17:52
 */
public class ForwardServer implements AutoCloseable {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    //连接客户端的channel
    private final Channel clientChannel;
    private final String aesKey;
    public String key;
    public int serverport;
    public String localhost;
    public int localport;

    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;

    private ChannelFuture channelFuture;


    public ForwardServer(Channel clientChannel, String aesKey, String key, int serverport, String localhost, int localport) throws InterruptedException {
        this.clientChannel = clientChannel;
        this.aesKey = aesKey;
        this.key = key;
        this.serverport = serverport;
        this.localhost = localhost;
        this.localport = localport;
        init();
    }

    public void init() throws InterruptedException {
        bossGroup = new NioEventLoopGroup(1); //用于处理服务器端接收客户端连接
        workerGroup = new NioEventLoopGroup(); //进行网络通信（读写）
        final ServerBootstrap bootstrap = new ServerBootstrap(); //辅助工具类，用于服务器通道的一系列配置
        bootstrap.group(bossGroup, workerGroup) //绑定两个线程组
                .option(ChannelOption.SO_BACKLOG, 128) //初始化服务器可连接队列大小 设置TCP缓冲区
                .option(ChannelOption.SO_SNDBUF, 32 * 1024) //设置发送数据缓冲大小
                .option(ChannelOption.SO_RCVBUF, 32 * 1024) //设置接受数据缓冲大小
                .childOption(ChannelOption.SO_KEEPALIVE, true) //保持连接活跃状态
                .channel(NioServerSocketChannel.class) //指定NIO的模式
                .childHandler(new ChannelInitializer<SocketChannel>() { //配置具体的数据处理方式
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        final String forKey = RanStrUtil.ranStr(32);
                        final JSONObject jsonObject = new JSONObject();
                        jsonObject.put("type", "forward");
                        jsonObject.put("forKey", forKey);
                        jsonObject.put("localhost", localhost);
                        jsonObject.put("localport", localport);
                        clientChannel.writeAndFlush(jsonObject);
                        logger.debug("收到一个转发请求key:{} port:{}->{}:{}", key, serverport, localhost, localport);
                        GlobalUtil.forward.put(forKey, new ForwardRecord(socketChannel, aesKey));

                        socketChannel.eventLoop().schedule(() -> {
                            if (socketChannel.isActive() && socketChannel.pipeline().get(ForWardHandler.class) == null) {
                                logger.debug("{}绑定转发处理器失败{}", forKey, socketChannel.remoteAddress());
                                GlobalUtil.forward.remove(forKey);
                                socketChannel.close();
                            }
                        }, 30, TimeUnit.SECONDS);
                        socketChannel.pipeline().addLast(new TemporaryStorageHandler());
                    }
                });
        channelFuture = bootstrap.bind(serverport).sync();
        logger.info("转发服务器开启 key:{} {}->{}:{}", key, serverport, localhost, localport);
    }

    @Override
    public void close() throws Exception {
        logger.info("转发服务器关闭 key:{} {}->{}:{}", key, serverport, localhost, localport);
        channelFuture.channel().close();
        if (bossGroup != null) bossGroup.shutdownGracefully();
        if (workerGroup != null) workerGroup.shutdownGracefully();
    }
}
